
<!doctype html>
<html lang="en" class="no-js">
  <head>
    
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width,initial-scale=1">
      
      
      
      <link rel="icon" href="../../../../static/images/favicon.png">
      <meta name="generator" content="mkdocs-1.3.0, mkdocs-material-8.2.8">
    
    
      
        <title>HBase 生产集群性能调优之路 - WL4G DOCS</title>
      
    
    
      <link rel="stylesheet" href="../../../../assets/stylesheets/main.644de097.min.css">
      
        
        <link rel="stylesheet" href="../../../../assets/stylesheets/palette.e6a45f82.min.css">
        
      
    
    
    
      
        
        
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
        <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
      
    
    
      <link rel="stylesheet" href="../../../../static/css/util.css">
    
    <script>__md_scope=new URL("../../../..",location),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
    
      

    
    
  </head>
  
  
    
    
      
    
    
    
    
    <body dir="ltr" data-md-color-scheme="default" data-md-color-primary="" data-md-color-accent="">
  
    
    
      <script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
    
    <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
    <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
    <label class="md-overlay" for="__drawer"></label>
    <div data-md-component="skip">
      
        
        <a href="#hbase" class="md-skip">
          Skip to content
        </a>
      
    </div>
    <div data-md-component="announce">
      
    </div>
    
      <div data-md-component="outdated" hidden>
        <aside class="md-banner md-banner--warning">
          
        </aside>
      </div>
    
    
      

<header class="md-header" data-md-component="header">
  <nav class="md-header__inner md-grid" aria-label="Header">
    <a href="../../../.." title="WL4G DOCS" class="md-header__button md-logo" aria-label="WL4G DOCS" data-md-component="logo">
      
  <img src="../../../../static/images/mylogo.jpeg" alt="logo">

    </a>
    <label class="md-header__button md-icon" for="__drawer">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2z"/></svg>
    </label>
    <div class="md-header__title" data-md-component="header-title">
      <div class="md-header__ellipsis">
        <div class="md-header__topic">
          <span class="md-ellipsis">
            WL4G DOCS
          </span>
        </div>
        <div class="md-header__topic" data-md-component="header-topic">
          <span class="md-ellipsis">
            
              HBase 生产集群性能调优之路
            
          </span>
        </div>
      </div>
    </div>
    
      <form class="md-header__option" data-md-component="palette">
        
          
          
          <input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="" data-md-color-accent=""  aria-label="Switch to dark mode"  type="radio" name="__palette" id="__palette_1">
          
            <label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 6H7c-3.31 0-6 2.69-6 6s2.69 6 6 6h10c3.31 0 6-2.69 6-6s-2.69-6-6-6zm0 10H7c-2.21 0-4-1.79-4-4s1.79-4 4-4h10c2.21 0 4 1.79 4 4s-1.79 4-4 4zM7 9c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
            </label>
          
        
          
          
          <input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="" data-md-color-accent=""  aria-label="Switch to light mode"  type="radio" name="__palette" id="__palette_2">
          
            <label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5m0 8a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z"/></svg>
            </label>
          
        
      </form>
    
    
      <div class="md-header__option">
        <div class="md-select">
          
          <button class="md-header__button md-icon" aria-label="Select language">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m12.87 15.07-2.54-2.51.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04M18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12m-2.62 7 1.62-4.33L19.12 17h-3.24z"/></svg>
          </button>
          <div class="md-select__inner">
            <ul class="md-select__list">
              
                <li class="md-select__item">
                  <a href="/en/" hreflang="en" class="md-select__link">
                    English
                  </a>
                </li>
                
                <li class="md-select__item">
                  <a href="/zh/" hreflang="zh" class="md-select__link">
                    简体中文
                  </a>
                </li>
                
            </ul>
          </div>
        </div>
      </div>
    
    
      <label class="md-header__button md-icon" for="__search">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
      </label>
      <div class="md-search" data-md-component="search" role="dialog">
  <label class="md-search__overlay" for="__search"></label>
  <div class="md-search__inner" role="search">
    <form class="md-search__form" name="search">
      <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
      <label class="md-search__icon md-icon" for="__search">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12z"/></svg>
      </label>
      <nav class="md-search__options" aria-label="Search">
        
        <button type="reset" class="md-search__icon md-icon" aria-label="Clear" tabindex="-1">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>
        </button>
      </nav>
      
        <div class="md-search__suggest" data-md-component="search-suggest"></div>
      
    </form>
    <div class="md-search__output">
      <div class="md-search__scrollwrap" data-md-scrollfix>
        <div class="md-search-result" data-md-component="search-result">
          <div class="md-search-result__meta">
            Initializing search
          </div>
          <ol class="md-search-result__list"></ol>
        </div>
      </div>
    </div>
  </div>
</div>
    
    
  </nav>
  
</header>
    
    <div class="md-container" data-md-component="container">
      
      
        
          
            
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
  <div class="md-tabs__inner md-grid">
    <ul class="md-tabs__list">
      
        
  
  


  
  
  
    <li class="md-tabs__item">
      <a href="../../../.." class="md-tabs__link">
        Getting Started
      </a>
    </li>
  

      
    </ul>
  </div>
</nav>
          
        
      
      <main class="md-main" data-md-component="main">
        <div class="md-main__inner md-grid">
          
            
              
              <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
                <div class="md-sidebar__scrollwrap">
                  <div class="md-sidebar__inner">
                    

  


  

<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
  <label class="md-nav__title" for="__drawer">
    <a href="../../../.." title="WL4G DOCS" class="md-nav__button md-logo" aria-label="WL4G DOCS" data-md-component="logo">
      
  <img src="../../../../static/images/mylogo.jpeg" alt="logo">

    </a>
    WL4G DOCS
  </label>
  
  <ul class="md-nav__list" data-md-scrollfix>
    
      
      
      

  
  
  
    
    <li class="md-nav__item md-nav__item--nested">
      
      
        <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_1" data-md-state="indeterminate" type="checkbox" id="__nav_1" checked>
      
      
      
      
        <label class="md-nav__link" for="__nav_1">
          Getting Started
          <span class="md-nav__icon md-icon"></span>
        </label>
      
      <nav class="md-nav" aria-label="Getting Started" data-md-level="1">
        <label class="md-nav__title" for="__nav_1">
          <span class="md-nav__icon md-icon"></span>
          Getting Started
        </label>
        <ul class="md-nav__list" data-md-scrollfix>
          
            
              
  
  
  
    <li class="md-nav__item">
      <a href="../../../.." class="md-nav__link">
        Introduction
      </a>
    </li>
  

            
          
            
              
  
  
  
    <li class="md-nav__item">
      <a href="../../../../ABOUT_CN/" class="md-nav__link">
        About
      </a>
    </li>
  

            
          
        </ul>
      </nav>
    </li>
  

    
  </ul>
</nav>
                  </div>
                </div>
              </div>
            
            
          
          <div class="md-content" data-md-component="content">
            <article class="md-content__inner md-typeset">
              
                


<h1 id="hbase">HBase 生产集群性能调优之路<a class="headerlink" href="#hbase" title="Permanent link">&para;</a></h1>
<ul>
<li><a href="https://blogs.wl4g.com/archives/653">关联参考: SPARK 集群生产性能调优之路</a></li>
</ul>
<h2 id="1-hbase-jvm">1. HBase 中 JVM 配置<a class="headerlink" href="#1-hbase-jvm" title="Permanent link">&para;</a></h2>
<p>在JVM中，默认情况下会设置minimum heap size 为 1/64 可用物理内存，并为maximum heap size设置 1/4 的物理可用内存（不过在Java8 之前，默认最大是1g）。当然，我们可以通过手动指定 JVM 参数，配置JVM的内存，例如：<code>-Xms10g -Xmx10g</code> 在HBase 中，也可以在 hbase-env.sh 中显示指定堆内存大小，例如：</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1"></a><a href="#__codelineno-0-1"><span class="linenos" data-linenos="1 "></span></a>The maximum amount of heap to use. Default is left to JVM default.
<a id="__codelineno-0-2" name="__codelineno-0-2"></a><a href="#__codelineno-0-2"><span class="linenos" data-linenos="2 "></span></a><span class="nb">export</span> <span class="nv">HBASE_HEAPSIZE</span><span class="o">=</span>1G
</code></pre></div>
<p>这里有个问题是：是否要同时设置-Xms 与 -Xmx为一相同的值？若是将他们设置为同一一个固定值，则它们的优缺点有：</p>
<ul>
<li>
<p>对于一些垃圾回收的算法，例如G1，将它们锁定为同一值会更好
不设置 -Xms可以让VM启动更快，但是接下来，需要在每轮gc 增长堆内存后，才会达到一个稳定状态。这样会导致在启动后，最开始会带来一些延迟，这个延时并不是我们在HBase中期望看到的，因为HBase提供的服务一般都是交互式服务，若是设置-Xms与-Xmx为同一值，则JVM在启动时会稍慢，不过在正常启动后，不会再有隐含的内存大小调整的情况，所以会更稳定。而在RegionServer 的进程中，我们可以在分配工作给它前，稍等一会而，以缓解启动进程的消耗。</p>
</li>
<li>
<p>对于HMaster进程，一般不需要太大内存，2GB 到 4GB就可以满足master的工作需求，因为它的对象分配与销毁的频率远小于RegionServer。</p>
</li>
<li>
<p>对于RegionServer 进程，若是物理机内存为64GB，则一开始可以给它分配10GB的-Xmx，之后可以根据监控（如jstat等）再进行向上调整。不过若是分配了较大的堆内存，我们也必须要考虑不同的GC算法，以支持更大堆内存中的垃圾回收。</p>
</li>
<li>
<p>基于以上场景，我们可以在hbase-env.sh 中配置以下参数：</p>
</li>
</ul>
<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1"></a><a href="#__codelineno-1-1"><span class="linenos" data-linenos="1 "></span></a><span class="c1"># Set environment variables here.</span>
<a id="__codelineno-1-2" name="__codelineno-1-2"></a><a href="#__codelineno-1-2"><span class="linenos" data-linenos="2 "></span></a><span class="nb">export</span> <span class="nv">HBASE_MASTER_OPTS</span><span class="o">=</span><span class="s2">&quot;-Xms2g -Xmx2g&quot;</span>
<a id="__codelineno-1-3" name="__codelineno-1-3"></a><a href="#__codelineno-1-3"><span class="linenos" data-linenos="3 "></span></a><span class="nb">export</span> <span class="nv">HBASE_REGIONSERVER_OPTS</span><span class="o">=</span><span class="s2">&quot;-Xms10g -Xmx10g&quot;</span>
</code></pre></div>
<p>如前文所述，不指定-Xms的话，堆内存需要在增长多次后才能到达最大堆内存，导致在重新分配内存时的不必要开销，所以最好是按上述指定。</p>
<ul>
<li>HBase中的堆内存管理</li>
</ul>
<p>在RegionServer中，主要的任务为：接收并处理读写请求。不过除此之外，RegionServer也需要在内存中维护一些内部信息，例如：对于每个打开的region来说，它们都需要在内存里结构化存储，以处理一些常规操作。</p>
<p>在RegionServer启动后，所分配的JVM堆内存大小就已经指定了，无法再增加。所以对于这部分已有的内存，我们需要谨慎使用，其中最重要的一部分就是：如何调整可供读写操作使用的可用内存。</p>
<p>在HBase 1.0 之前，主要有两个参数需要调整，分别为memstores（对于写）以及block cache（对于读）。剩下的内存被用于所有其他杂项使用（例如Java用于存储打开的region的结构）。对于memstore，我们有以下两个参数可以指定：</p>
<p><code>hbase.regionserver.global.memstore.upperLimit</code> 默认为0.4（40%），也就是memstore可以使用的最大堆内存，
<code>hbase.regionserver.global.memstore.lowerLimit</code> 默认为0.35（30%），也就是memstore可以使用的最小堆内存，
upperLimit 在这里较为重要，因为它指定了对于写操作，可以使用的最大内存。对于读来说，仅有一个参数可供调整：</p>
<p><code>hfile.block.cache.size</code> 默认为0.4（40%），堆内存中用于分配给block cache的比率
memestore 的 upperLimit ，与 blockcache 的大小，它们的和不能超过80% 堆内存总大小，因为仍需要提供剩余的20% 内存以上，用于regionserver进程完成它自身的部分工作（并且防止Java OOM的问题）。</p>
<p>在HBase 1.0 之后，提供了更多的配置项，用于指定memstore 与 block cache 的upper limit 与 lower limit，例如：</p>
<p><code>hbase.regionserver.global.memstore.size.min.range</code> 默认未设置，为所有memstore的最小分配内存
<code>hbase.regionserver.global.memstore.size.max.range</code> 默认未设置，为所有memsotre的最大分配内存
<code>hbase.regionserver.global.memstore.size</code> 默认为0.4（40%），也就是memstore默认被分配的对内存比例</p>
<p><code>hfile.block.cache.size.min.range</code> 默认未设置，为block cache 大小的最小值
<code>hfile.block.cache.size.max.range</code> 默认未设置，为block cache 大小的最大值
<code>hfile.block.cache.size</code> 默认为0.4（40%），也就是堆内存中默认分配给block cache 的比例</p>
<p><code>hbase.regionserver.heapmemory.tuner.period</code> 默认为60000（60s），定义heap tunner 运行的频率
对于memstore与block cache 的上限与下限，我们必须做配置，除非禁止tuner。</p>
<p>HBase 提供了一个机制是：根据请求流量大小，对堆内存进行动态调整。这就是为什么在开启tuner的情况下，我们必须要设置一个上限与下限。</p>
<p>不过这里仍需要保证的一点是：memstore 与 block cache 的最大使用量不能超过JVM内存的 80%。如果超过了这个比例，则tuner会报错，region server 会退出。这个限制的原因与之前的一样：需要剩余足够的内存供给 region server 进程正常工作。</p>
<p>下图是固定内存与动态内存的一个示例图：</p>
<p><img alt="" src="https://blogs.wl4g.com:443/wp-content/uploads/2021/09/hbase1.png" /></p>
<p>动态内存对应的配置为：</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1"></a><a href="#__codelineno-2-1"><span class="linenos" data-linenos=" 1 "></span></a><span class="nt">&lt;property&gt;</span>
<a id="__codelineno-2-2" name="__codelineno-2-2"></a><a href="#__codelineno-2-2"><span class="linenos" data-linenos=" 2 "></span></a>  <span class="nt">&lt;name&gt;</span>hbase.regionserver.global.memstore.size.min.range<span class="nt">&lt;/name&gt;</span>
<a id="__codelineno-2-3" name="__codelineno-2-3"></a><a href="#__codelineno-2-3"><span class="linenos" data-linenos=" 3 "></span></a>  <span class="nt">&lt;value&gt;</span>0.2<span class="nt">&lt;/value&gt;</span>
<a id="__codelineno-2-4" name="__codelineno-2-4"></a><a href="#__codelineno-2-4"><span class="linenos" data-linenos=" 4 "></span></a><span class="nt">&lt;/property&gt;</span>
<a id="__codelineno-2-5" name="__codelineno-2-5"></a><a href="#__codelineno-2-5"><span class="linenos" data-linenos=" 5 "></span></a><span class="nt">&lt;property&gt;</span>
<a id="__codelineno-2-6" name="__codelineno-2-6"></a><a href="#__codelineno-2-6"><span class="linenos" data-linenos=" 6 "></span></a>  <span class="nt">&lt;name&gt;</span>hbase.regionserver.global.memstore.size.max.range<span class="nt">&lt;/name&gt;</span>
<a id="__codelineno-2-7" name="__codelineno-2-7"></a><a href="#__codelineno-2-7"><span class="linenos" data-linenos=" 7 "></span></a>  <span class="nt">&lt;value&gt;</span>0.6<span class="nt">&lt;/value&gt;</span>
<a id="__codelineno-2-8" name="__codelineno-2-8"></a><a href="#__codelineno-2-8"><span class="linenos" data-linenos=" 8 "></span></a><span class="nt">&lt;/property&gt;</span>
<a id="__codelineno-2-9" name="__codelineno-2-9"></a><a href="#__codelineno-2-9"><span class="linenos" data-linenos=" 9 "></span></a><span class="nt">&lt;property&gt;</span>
<a id="__codelineno-2-10" name="__codelineno-2-10"></a><a href="#__codelineno-2-10"><span class="linenos" data-linenos="10 "></span></a>  <span class="nt">&lt;name&gt;</span>hfile.block.cache.size.min.range<span class="nt">&lt;/name&gt;</span>
<a id="__codelineno-2-11" name="__codelineno-2-11"></a><a href="#__codelineno-2-11"><span class="linenos" data-linenos="11 "></span></a>  <span class="nt">&lt;value&gt;</span>0.2<span class="nt">&lt;/value&gt;</span>
<a id="__codelineno-2-12" name="__codelineno-2-12"></a><a href="#__codelineno-2-12"><span class="linenos" data-linenos="12 "></span></a><span class="nt">&lt;/property&gt;</span>
<a id="__codelineno-2-13" name="__codelineno-2-13"></a><a href="#__codelineno-2-13"><span class="linenos" data-linenos="13 "></span></a><span class="nt">&lt;property&gt;</span>
<a id="__codelineno-2-14" name="__codelineno-2-14"></a><a href="#__codelineno-2-14"><span class="linenos" data-linenos="14 "></span></a>  <span class="nt">&lt;name&gt;</span>hfile.block.cache.size.max.range<span class="nt">&lt;/name&gt;</span>
<a id="__codelineno-2-15" name="__codelineno-2-15"></a><a href="#__codelineno-2-15"><span class="linenos" data-linenos="15 "></span></a>  <span class="nt">&lt;value&gt;</span>0.6<span class="nt">&lt;/value&gt;</span>
<a id="__codelineno-2-16" name="__codelineno-2-16"></a><a href="#__codelineno-2-16"><span class="linenos" data-linenos="16 "></span></a><span class="nt">&lt;/property&gt;</span>
</code></pre></div>
<p>它们的初始值为默认的40%内存。Tuner可以在某一个内存（如memstore的内存）达到最小20%后，给另一个分配60%的内存。</p>
<p>Tuner的机制仅是增加某一区域（如memstore）的内存，在增加后另一区域（如block cache）的内存即会减少。基于的是（或是说触发的条件是）：由于内存压力导致的memstore 强制刷新（会要求更多内存，即增加此区域内存），或是内存压力导致的block cache 中的cache 置换。</p>
<ul>
<li>两个主要GC算法，在 HBase 中，主要使用的GC算法为CMS或G1，它们的对比如下：</li>
</ul>
<table>
<thead>
<tr>
<th>JVM GC</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>-XX:+UseSerialGC</td>
<td>串行回收器，适用嵌入式小进程低并发，这个参数会使新生代和老年代都使用串行回收器，新生代使用复制算法，老年代使用标记-整理算法。Serial收集器是最基本、历史最悠久的收集器，它是一个单线程收集器。一旦回收器开始运行时，整个系统都要停止。Client模式下默认开启，其他模式默认关闭。</td>
</tr>
<tr>
<td>-XX:+UseParNewGC</td>
<td>ParNew收集器是Serial收集器的多线程版本，Parallel是并行的意思，ParNew收集器是Serial收集器的多线程版本，使用这个参数后会在新生代进行并行回收，老年代仍旧使用串行回收。新生代S区任然使用复制算法。操作系统是多核CPU上效果明显，单核CPU建议使用串行回收器。打印GC详情时ParNew标识着使用了ParNewGC回收器。默认关闭。</td>
</tr>
<tr>
<td>-XX:+UseParallelGC</td>
<td>代表新生代使用Parallel收集器，老年代使用串行收集器。Parallel Scavenge收集器在各个方面都很类似ParNew收集器，它的目的是达到一个可以控制的吞吐量。吞吐量为CPU用于运行用户代码的时间与CPU总消耗时间的比值，即吞吐量=运行用户代码时间/（运行用户代码时间+垃圾收集时间），虚拟机运行100分钟，垃圾收集花费1分钟，那吞吐量就99%。Server模式默认开启，其他模式，默认关闭。Parallel Scavenge提供了两个参数用于控制吞吐量，-XX:MaxGCPauseMillis 参数用于设置最大停顿时间，它的参数运行值是一个大于0的毫秒数，收集器将尽力保证垃圾回收时间不超过设定值，系统运行的需要回收的垃圾总量是固定的，缩短停顿时间的同时会增大回收频度。-XX:GCTimeRatio 参数用户控制垃圾回收时间占比，它运行的参数值是0-100的整数，如果参数设置为19，代表最大GC时间占总时间的5%（1/(1+19)）。Parallel收集器还提供了自适应的调节策略 -XX:UseAdaptiveSizePolicy 即JVM会根据实际运行情况动态调整新生代大小、新生代和s区比例、晋升老年代对象大小等细节参数</td>
</tr>
<tr>
<td>-XX:+UseParallelOldGC</td>
<td>新生代和老年代都使用并行收集器。打印出的GC会带PSYoungGen、ParOldGen关键字</td>
</tr>
<tr>
<td>-XX:+UseConcMarkSweepGC</td>
<td>低延时（堆内存 &lt; 32GB，Concurrent Mark Sweep 并发标记清除，即使用CMS收集器。它是和应用程序线程一起执行，相对于Stop The World来说虚拟机停顿时间较少。停顿减少，吞吐量会降低。它使用的是 标记清除算法，运作过程为四个步骤，分别是 初始标记—并发标识—重新标记—并发清除。它是老年代的收集算法，新生代使用ParNew收集算法。默认关闭。CMS收集器的缺点是对服务器CPU资源较为敏感，在并发标记时会降低吞吐量。它使用的标记清除算法也会产生大量空间碎片，空间碎片的存在会加大Full GC的频率，虽然老年代还有足够的内存，但是因为内存空间连续，不得不进行Full GC。）</td>
</tr>
<tr>
<td>-XX:+UseG1GC</td>
<td>适用高并发低延时，号称通过 region 解决了内存碎片问题，但实际上并无完全解决，设计哲学是想要多快就多快，大不了只回收1%也不影响业务线程，see: <a href="https://www.oracle.com/cn/technical-resources/articles/java/g1gc.html">https://www.oracle.com/cn/technical-resources/articles/java/g1gc.html</a></td>
</tr>
<tr>
<td>-XX:+UseZGC</td>
<td>适用高并发低延时（要求 Heap Memory &gt;= 32GB），see:<a href="https://wiki.openjdk.java.net/display/zgc/Main#Main-EnablingZGC">https://wiki.openjdk.java.net/display/zgc/Main#Main-EnablingZGC</a></td>
</tr>
</tbody>
</table>
<p>CMS为较早使用的GC 算法，它可以有效提升HBase的性能，特别是I/O请求的延时。不过它的缺点是：有最大内存的限制。</p>
<p>在CMS刚开始被提出的时候，它可以良好适应当时的服务器硬件性能，不过随着现在超出1TB的内存机器越来越常见，32GB大小的堆内存配置也越来越常见。CMS不适用于较大堆内存的原因是因为：在较大的堆内存中，垃圾回收会造成较长时间的停顿（几秒到十几秒），并最终会导致请求的高延时。</p>
<ul>
<li>JVM stop world log</li>
</ul>
<p>在HBase region server 进程启动后，会启动一个轻量级的线程，定期（500ms）被唤醒，并比较它在唤醒前它睡眠的时间以及实际经过的时间是否一致。如果检测到它实际睡眠的时间超过了500ms，则在服务器日志记录，例如：</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1"></a><a href="#__codelineno-3-1"><span class="linenos" data-linenos="1 "></span></a>2018-08-05 07:11:32,390 INFO  [JvmPauseMonitor] util.JvmPauseMonitor: Detected pause in JVM or host machine (eg GC): pause of approximately 7448ms
<a id="__codelineno-3-2" name="__codelineno-3-2"></a><a href="#__codelineno-3-2"><span class="linenos" data-linenos="2 "></span></a>GC pool &#39;ParNew&#39; had collection(s): count=1 time=49ms
<a id="__codelineno-3-3" name="__codelineno-3-3"></a><a href="#__codelineno-3-3"><span class="linenos" data-linenos="3 "></span></a>GC pool &#39;ConcurrentMarkSweep&#39; had collection(s): count=1 time=7700ms
<a id="__codelineno-3-4" name="__codelineno-3-4"></a><a href="#__codelineno-3-4"><span class="linenos" data-linenos="4 "></span></a>...
<a id="__codelineno-3-5" name="__codelineno-3-5"></a><a href="#__codelineno-3-5"><span class="linenos" data-linenos="5 "></span></a>2018-08-11 23:01:56,686 WARN  [JvmPauseMonitor] util.JvmPauseMonitor: Detected pause in JVM or host machine (eg GC): pause of approximately 3594293ms No GCs detected
</code></pre></div>
<p>第一条是由 gc 导致，记录的是 CMS 消耗了超过7秒等待垃圾回收完成。第二条与机器的暂停无关。根据暂停的时间，日志会以INFO或是WARN级别记录下来，对应的参数如下：</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1"></a><a href="#__codelineno-4-1"><span class="linenos" data-linenos="1 "></span></a>jvm.pause.info-threshold.ms：默认为1000ms（1s），若是停顿时间超过此值，则以INFO级别记录日志
<a id="__codelineno-4-2" name="__codelineno-4-2"></a><a href="#__codelineno-4-2"><span class="linenos" data-linenos="2 "></span></a>jvm.pause.warn-threshold.ms：默认为10000ms（10s），若是停顿时间超过此值，则以WARN级别记录日志
</code></pre></div>
<p>在做调优时要注意这些日志条目，以了解停顿的频率与时间。并根据时间调整相关timeout配置（例如zookeeper的配置，如果regionserver 超过一段时间未响应，可能会被zookeeper认为宕机）。</p>
<blockquote>
<p>据生产真实经历，使用的阿里云 EMR，频繁出现 HRegionServer 挂掉被自动重启，当时工单联系运维工程师他们基本分析是因为数据量过大 (当时确实增加了部分iot设备上行数据)，由于客户投诉十万火急，快速解决方法就是增大 HR 的 <code>-Xms4096m -Xmx4096m -Xmn256m</code> 翻倍或者半倍，然后重启监控观察，当时的环境最终 HR 宕机频率立马就低了很多，生产环境紧急情况可参考，但终究还是得个方位优化才行。</p>
</blockquote>
<ul>
<li>CMS 与 G1</li>
</ul>
<p>再回到GC部分，G1的提出就是为了解决：CMS在较大堆内存回收垃圾时产生的长时间停顿问题。下图是两者的实现与对比：</p>
<p><img alt="" src="https://blogs.wl4g.com:443/wp-content/uploads/2021/09/hbase2.png" /></p>
<p>在G1 中，相对于CMS预先配置的堆内存容量，它将整个堆内存空间划分为多个相同大小的regions，一般是2000个左右。在划分好后，G1可以让其中的部分region分别执行Young Generation、Old Generation、以及survivor的角色。G1 gc采用的策略就是在这些区域中执行较多的gc任务，以尽可能地减少jvm pause以及对用户的影响。虽然这些额外的工作是会对延时有较大的影响，不过从长远来看，它的jvm pause时间会更短，并可以提供更稳健的延时，而不是抖动的延时。</p>
<ul>
<li>CMS</li>
</ul>
<p>CMS是一个性能较好的GC算法，已被使用多年。启用的参数为：<code>-XX:+UseParNewGC -XX:+UseConcMarkSweepGC</code></p>
<p>（这里其实在指定 -XX:+UseConcMarkSweepGC 后就默认已经指定了-XX:+UseParNewGC，这里显示指定为了之后进一步的讨论）</p>
<p>第一个选项是为Young Generation 设置使用Parallel New Collector：它在清理Young Generation 时会停止整个Java 进程。这个在Young Generation中是可以被接受的，因为它的内存相对Old Generation 会少得多，所以此过程也不会消耗太多的时间，一般少于几百毫秒。不过，这个设置对于Old Generation是不合适的，在最坏的情况下，可能会导致多达几秒的延时。</p>
<p>对此，CMS可以较好地解决此问题，它会尽可能地并行做gc，而不停止Java 进程。虽然此方法会造成额外的CPU资源消耗，但是可以减少或是避免JVM pause。CMS也是hbase 中默认的gc算法。</p>
<p>在CMS中，我们可以做的第一个调整是：Young Generation的大小。它默认的值较小，在region server 执行写操作时，会将数据写入memstore，过小的Young Generation会让这部分数据停留在此区域的时间较短，并可能造成不必要的generation 提升（Young Generation 提升到 Old Generation）。对此，可以将Young Generation的大小设置为一个固定的、更大的一个值，以减少此类影响，例如：</p>
<p><code>-XX:MaxNewSize=128m -XX:NewSize=128m</code></p>
<p>或者也可以直接用这两个配置的简写：-Xmn128m。</p>
<p>128MB是一个较好的起点，之后可以根据JVM的指标，确定是否满足需求或是是否需要添加更多的内存。不过对于有较高负载的region server 来说，默认的Young Generation 的大小有些过于小了。如果在一开始不调整此参数的话，我们可能会注意到CPU的使用率会偶尔突然增高，因为JVM需要花时间将Young Generation 中的对象提升到Old Generation中（由于Young Generation中内存不足）。</p>
<p>CMS还有一个参数用于控制：什么时候开始做concurrent mark and sweep 检查。这个参数可以通过以下配置设置：</p>
<p><code>-XX:CMSInitiatingOccupancyFraction=85</code></p>
<p>此参数指定的是：什么时候启动后台cms垃圾回收。此参数不能配置过高，为了避免concurrent mode failure 问题。此问题发生的场景为：在后台mark and sweep 工作正在进行时，堆内存已经耗尽了所有的可用空间。则此时，JRE必须停止Java 进程，并且通过清除垃圾，或是将Young Generation 中的对象移动到 Old Generation中（如果这些对象已经够old），以释放空间。</p>
<p>设置此值为85%，表示在堆内存耗尽之前就会开始执行concurrent collection 的工作，但也不会是太早去做这个工作。一个常规的建议是：将这部分值设置为稍高于memstore与block cache总和大小的值。它们的总和一般为80%（memstore 40%，block cache 40%），所以我们会优先设置为85%左右或以上。</p>
<p>下面是一个起始的CMS参考配置：</p>
<p><code>export HBASE_REGIONSERVER_OPTS="-Xms10g -Xmx10g -Xmn128m \
  -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 \
  -XX:+UseCMSInitiatingOccupancyOnly"</code></p>
<ul>
<li>G1</li>
</ul>
<p>G1的启用方法为：<code>-XX:+UseG1GC</code></p>
<p>除此之外，还需要设置一个预期最大的gc pause 时间。不过这个仅是一个soft goal，JVM会尽量满足此目标。不过若是为了满足工作负载，此暂停时间可能会更长。此参数为：<code>-XX:MaxGCPauseMillis=200</code></p>
<p>默认即为200ms，可以根据需求做进一步调整。最后一个参数需要调整的是：什么时候gc collector 开始一个concurrent GC cycle，以整个堆内存已使用部分百分比为阈值，默认为45%，例如：<code>-XX:InitiatingHeapOccupancyPercent=45</code></p>
<p>可以先以此为初始配置，然后再根据负载调整G1 中每个region的大小等，此部分需要更深入的G1 算法知识，之后再详述。</p>
<p>另一个可以用于参考的配置是 HubSpot 发布的一个一篇 HBase 中 G1 GC 调优文档：<a href="https://product.hubspot.com/blog/g1gc-tuning-your-hbase-cluster">https://product.hubspot.com/blog/g1gc-tuning-your-hbase-cluster</a>，根据文档中的建议，我们可以尝试使用以下配置：</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1"></a><a href="#__codelineno-5-1"><span class="linenos" data-linenos="1 "></span></a>-XX:MaxGCPauseMillis=50
<a id="__codelineno-5-2" name="__codelineno-5-2"></a><a href="#__codelineno-5-2"><span class="linenos" data-linenos="2 "></span></a>-XX:+UnlockExperimentalVMOptions
<a id="__codelineno-5-3" name="__codelineno-5-3"></a><a href="#__codelineno-5-3"><span class="linenos" data-linenos="3 "></span></a>-XX:-OmitStackTraceInFastThrow
<a id="__codelineno-5-4" name="__codelineno-5-4"></a><a href="#__codelineno-5-4"><span class="linenos" data-linenos="4 "></span></a>-XX:+ParallelRefProcEnabled
<a id="__codelineno-5-5" name="__codelineno-5-5"></a><a href="#__codelineno-5-5"><span class="linenos" data-linenos="5 "></span></a>-XX:+PerfDisableSharedMem
<a id="__codelineno-5-6" name="__codelineno-5-6"></a><a href="#__codelineno-5-6"><span class="linenos" data-linenos="6 "></span></a>-XX:-ResizePLAB
<a id="__codelineno-5-7" name="__codelineno-5-7"></a><a href="#__codelineno-5-7"><span class="linenos" data-linenos="7 "></span></a>-XX:ParallelGCThreads=8 + (&lt;no. of logical processors&gt; - 8) * (5/8)
</code></pre></div>
<p>另一方面，根据另一篇 Intel 团队文档：https://software.intel.com/en-us/blogs/2014/06/18/part-1-tuning-java-garbage-collection-for-hbase
对 Young Generation 的调优的建议为（默认 Young Generation 的比例为 5%）：</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1"></a><a href="#__codelineno-6-1"><span class="linenos" data-linenos="1 "></span></a>32GB heap, -XX:G1NewSizePercent=3;
<a id="__codelineno-6-2" name="__codelineno-6-2"></a><a href="#__codelineno-6-2"><span class="linenos" data-linenos="2 "></span></a>64GB heap, -XX:G1NewSizePercent=2;
<a id="__codelineno-6-3" name="__codelineno-6-3"></a><a href="#__codelineno-6-3"><span class="linenos" data-linenos="3 "></span></a>100GB and above heap, -XX:G1NewSizePercent=1;
</code></pre></div>
<p>最终 Intel 给出的 region server JVM 的参数为：</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1"></a><a href="#__codelineno-7-1"><span class="linenos" data-linenos="1 "></span></a>-XX:+UseG1GC
<a id="__codelineno-7-2" name="__codelineno-7-2"></a><a href="#__codelineno-7-2"><span class="linenos" data-linenos="2 "></span></a>-Xms100g -Xmx100g # (Heap size used in our tests)
<a id="__codelineno-7-3" name="__codelineno-7-3"></a><a href="#__codelineno-7-3"><span class="linenos" data-linenos="3 "></span></a>-XX:MaxGCPauseMillis=100 # (Desired GC pause time in tests)
<a id="__codelineno-7-4" name="__codelineno-7-4"></a><a href="#__codelineno-7-4"><span class="linenos" data-linenos="4 "></span></a>-XX:+ParallelRefProcEnabled
<a id="__codelineno-7-5" name="__codelineno-7-5"></a><a href="#__codelineno-7-5"><span class="linenos" data-linenos="5 "></span></a>-XX:-ResizePLAB
<a id="__codelineno-7-6" name="__codelineno-7-6"></a><a href="#__codelineno-7-6"><span class="linenos" data-linenos="6 "></span></a>-XX:ParallelGCThreads= 8+(40-8)(5/8)=28
<a id="__codelineno-7-7" name="__codelineno-7-7"></a><a href="#__codelineno-7-7"><span class="linenos" data-linenos="7 "></span></a>-XX:G1NewSizePercent=1
</code></pre></div>
<p>针对这里的建议是：对 Young Generation，使用 Intel 的配置；对 RegionServer 其他的配置，采用 HubSpot 的配置（为2016年发布，Intel 对 RegionServer 的配置建议为 2014 年发布）。</p>
<h2 id="_1">参考资料<a class="headerlink" href="#_1" title="Permanent link">&para;</a></h2>
<ul>
<li>
<p><a href="https://www.cnblogs.com/zackstang/p/11573719.html">参考1 Thanks to the author, reprinted from: https://www.cnblogs.com/zackstang/p/11573719.html</a></p>
</li>
<li>
<p><a href="https://zhuanlan.zhihu.com/p/111606398">参考2: 全面认识HBase架构</a></p>
</li>
<li>
<p><a href="https://blog.csdn.net/kntao/article/details/44595357">参考3: HBase 生产集群间歇性停顿 10s 分析调优</a></p>
</li>
</ul>

              
            </article>
          </div>
        </div>
        
      </main>
      
        <footer class="md-footer">
  
  <div class="md-footer-meta md-typeset">
    <div class="md-footer-meta__inner md-grid">
      <div class="md-copyright">
  
  
    Made with
    <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
      Material for MkDocs
    </a>
  
</div>
      
    </div>
  </div>
</footer>
      
    </div>
    <div class="md-dialog" data-md-component="dialog">
      <div class="md-dialog__inner md-typeset"></div>
    </div>
    <script id="__config" type="application/json">{"base": "../../../..", "features": ["search.suggest", "search.highlight", "navigation.tabs", "navigation.expand", "toc.follow", "toc.integrate"], "search": "../../../../assets/javascripts/workers/search.5e67fbfe.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.config.lang": "en", "search.config.pipeline": "trimmer, stopWordFilter", "search.config.separator": "[\\s\\-]+", "search.placeholder": "Search", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version.title": "Select version"}, "version": {"default": "latest", "provider": "mike"}}</script>
    
    
      <script src="../../../../assets/javascripts/bundle.c44cc438.min.js"></script>
      
        <script src="../../../../static/js/util.js"></script>
      
    
  </body>
</html>